/**********************************************************************\

 Copyright 2012 Diggory Blake

 This file is part of klip-engine

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

\**********************************************************************/
#pragma once

template<typename T, bool allowShare = false> class kSharedResourcesBase abstract : public kBase {
protected:
	typedef typename T::Container Container;
	typedef typename T::Key Key;

	struct SharedResource {
		int refs;
		T* resource;
		inline SharedResource(T* resource) {
			this->resource = resource;
			this->refs = 1;
		}
	};

	Container* _container;
	std::map<Key, SharedResource> _sharedResources;
	inline const T* share(const Key& key, T* resource) {
		if (!resource)
			return 0;

		if (_sharedResources.insert(std::make_pair(key, SharedResource(resource))).second) {
			resource->_sharedKey = key;
			return resource;
		}
		return 0;
	}
	kSharedResourcesBase(Container* container) {
		_container = container;
	}

public:
	inline const T* get(const Key& key, bool autoCreate = true) {
		auto i = _sharedResources.find(key);
		if (i != _sharedResources.end()) {
			i->second.refs++;
			return i->second.resource;
		}

		if (autoCreate) {
			return share(key, T::createShared(_container, key));
		}

		return 0;
	}
	inline void release(const T* resource) {
		auto i = _sharedResources.find(resource->_sharedKey);
		if (i != _sharedResources.end()) {
			if (!--i->second.refs) {
				T::destroyShared(_container, i->second.resource);
				_sharedResources.erase(i);
			}
		}
	}
};
template<typename T> class kSharedResourcesBase<T, true> abstract : public kSharedResourcesBase<T, false> {
protected:
	inline kSharedResourcesBase(Container* container) : kSharedResourcesBase<T, false>(container) { }
public:
	inline const T* share(const Key& key, T* resource) {
		kSharedResourcesBase<T, false>::share(key, resource);
	}
};
template<typename T> class kSharedResources : public kSharedResourcesBase<T, T::AllowShare> {
public:
	inline kSharedResources(Container* container) : kSharedResourcesBase<T, T::AllowShare>(container) { }
};

template<typename K, typename C = void> class kSharedResource abstract {
	template<typename T, bool B> friend class kSharedResourcesBase;

protected:
	typedef C Container;
	typedef K Key;
	static const bool AllowShare = false;

private:
	Key _sharedKey;

	static inline kSharedResource* createShared(Container* container, const Key& key) {
		throw kError(_T("Not implemented!"));
	}
	static inline void destroyShared(Container* container, kSharedResource* resource) {
		throw kError(_T("Not implemented!"));
	}

protected:
	inline ~kSharedResource() { };

public:
	inline const K& getSharedKey() {
		return _sharedKey;
	}
};
